package main

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"time"
)

type Condition struct {
	Key string
	Val string
	Op  string
}

type ConditionSet map[string][]Condition

func ParseCondition(conds []string) (ConditionSet, error) {
	ret := make(ConditionSet)
	if len(conds) == 0 {
		return nil, nil
	}
	for _, cond := range conds {
		opIndex := -1
		if strings.Index(cond, ">") > 0 {
			opIndex = strings.Index(cond, ">")
		} else if strings.Index(cond, "=") > 0 {
			opIndex = strings.Index(cond, "=")
		} else if strings.Index(cond, "<") > 0 {
			opIndex = strings.Index(cond, "<")
		} else {
			return nil, nil
		}
		if opIndex <= 0 || opIndex == len(cond)-1 {
			return nil, fmt.Errorf("bad query condition: %v", cond)
		}
		cnd := Condition{
			Key: cond[0:opIndex],
			Val: cond[opIndex+1:],
			Op:  cond[opIndex : opIndex+1],
		}
		ret[cnd.Key] = append(ret[cnd.Key], cnd)
	}
	if !ret.IsValid() {
		return nil, fmt.Errorf("bad query conditions: %v", conds)
	}
	return ret, nil
}

func (cs ConditionSet) IsValid() bool {
	for key, conds := range cs {
		if !Contains(IndexList, key) {
			return false
		}
		if key == "DATETIME" && len(conds) > 2 {
			return false
		}
		if key == "DATETIME" && len(conds) == 2 {
			if conds[0].Op == conds[1].Op {
				return false
			}
			if Contains([]string{conds[0].Op, conds[1].Op}, "=") {
				return false
			}
		}
		if key != "DATETIME" && len(conds) != 1 {
			return false
		}
		if key != "DATETIME" && conds[0].Op != "=" {
			return false
		}
	}
	return true
}

func (cs ConditionSet) ParseDateTime() (time.Time, time.Time, error) {
	var mintime, maxtime time.Time
	if cs["DATETIME"] == nil {
		return mintime, maxtime, nil
	}
	mintime, maxtime, err := getTimeRange(cs["DATETIME"])
	if err != nil {
		return mintime, maxtime, err
	}
	datelist := getDatesInRange(mintime, maxtime)
	if len(datelist) == 0 {
		delete(cs, "DATETIME")
		return mintime, maxtime, nil
	}
	var dtConds []Condition
	cond := Condition{
		Key: "DATETIME",
		Op:  "=",
	}
	for _, date := range datelist {
		cond.Val = date
		dtConds = append(dtConds, cond)
	}
	cs["DATETIME"] = dtConds
	return mintime, maxtime, nil
}

func getTimeRange(conds []Condition) (time.Time, time.Time, error) {
	min := ""
	max := ""
	var mintime time.Time
	var maxtime time.Time
	for _, cond := range conds {
		if cond.Op == ">" {
			min = cond.Val
		} else if cond.Op == "<" {
			max = cond.Val
		} else {
			max, min = cond.Val, cond.Val
		}
	}
	switch len(min) {
	case 0:
		year := time.Now().Year()
		min = strconv.Itoa(year) + "0101000000000"
	case 14:
		min = min + "000"
	}
	switch len(max) {
	case 0:
		max = time.Now().Format("20060102150405000")
	case 14:
		max = max + "000"
	}
	mintime, err := time.Parse("20060102150405000", min)
	if err != nil {
		return mintime, maxtime, err
	}
	maxtime, err = time.Parse("20060102150405000", max)
	if err != nil {
		return mintime, maxtime, err
	}
	return mintime, maxtime, nil
}

func getDatesInRange(lower, upper time.Time) []string {
	var datelist []string
	cur := lower
	date := cur.Format("200601")
	for !cur.After(upper) {
		datelist = append(datelist, date)
		cur = cur.AddDate(0, 1, 0)
		date = cur.Format("200601")
	}
	return datelist
}

func (cs ConditionSet) BuildQueryIndex(datetime string) ([]byte, error) {
	index := make(map[string]string)
	for key, cond := range cs {
		index[key] = cond[0].Val
	}
	if index["DATETIME"] != "" {
		index["DATETIME"] = datetime
	}
	return json.Marshal(index)
}

func Contains(slice []string, element string) bool {
	for _, s := range slice {
		if s == element {
			return true
		}
	}
	return false
}
